<template>
  <ul :class="getClass">
    <slot></slot>
  </ul>
</template>

<script lang="ts" setup>
import type { PropType } from "vue";
import type { SubMenuProvider } from "./types";
import {
  ref,
  computed,
  onMounted,
  watchEffect,
  watch,
  nextTick,
  getCurrentInstance,
  provide,
} from "vue";
import { useDesign } from "@/hooks/web/useDesign";
import { propTypes } from "@/utils/propTypes";
import {
  createSimpleRootMenuContext,
  type MenuEmitterEvents,
} from "./useSimpleMenuContext";
import { mitt } from "@/utils/mitt";

defineOptions({ name: "Menu" });

const props = defineProps({
  theme: propTypes.oneOf(["light", "dark"]).def("light"),
  activeName: propTypes.oneOfType([propTypes.string, propTypes.number]),
  openNames: {
    type: Array as PropType<string[]>,
    default: () => [],
  },
  accordion: propTypes.bool.def(true),
  width: propTypes.string.def("100%"),
  collapsedWidth: propTypes.string.def("48px"),
  indentSize: propTypes.number.def(16),
  collapse: propTypes.bool.def(true),
  activeSubMenuNames: {
    type: Array as PropType<(string | number)[]>,
    default: () => [],
  },
});

const emit = defineEmits(["select", "open-change"]);

const rootMenuEmitter = mitt<MenuEmitterEvents>();
const instance = getCurrentInstance();

const currentActiveName = ref<string | number>("");
const openedNames = ref<(string | number)[]>([]);

const { prefixCls } = useDesign("menu");

const isRemoveAllPopup = ref(false);

createSimpleRootMenuContext({
  rootMenuEmitter: rootMenuEmitter,
  activeName: currentActiveName,
});

const getClass = computed(() => {
  const { theme } = props;
  return [
    prefixCls,
    `${prefixCls}-${theme}`,
    `${prefixCls}-vertical`,
    {
      [`${prefixCls}-collapse`]: props.collapse,
    },
  ];
});

watchEffect(() => {
  openedNames.value = props.openNames;
});

watchEffect(() => {
  if (props.activeName) {
    currentActiveName.value = props.activeName;
  }
});

watch(
  () => props.openNames,
  () => {
    nextTick(() => {
      updateOpened();
    });
  },
);

function updateOpened() {
  rootMenuEmitter.emit("on-update-opened", openedNames.value);
}

function addSubMenu(name: string | number) {
  if (openedNames.value.includes(name)) return;
  openedNames.value.push(name);
  updateOpened();
}

function removeSubMenu(name: string | number) {
  openedNames.value = openedNames.value.filter((item) => item !== name);
  updateOpened();
}

function removeAll() {
  openedNames.value = [];
  updateOpened();
}

function sliceIndex(index: number) {
  if (index === -1) return;
  openedNames.value = openedNames.value.slice(0, index + 1);
  updateOpened();
}

provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
  addSubMenu,
  removeSubMenu,
  getOpenNames: () => openedNames.value,
  removeAll,
  isRemoveAllPopup,
  sliceIndex,
  level: 0,
  props: props as any,
});

onMounted(() => {
  openedNames.value = !props.collapse ? [...props.openNames] : [];
  updateOpened();
  rootMenuEmitter.on("on-menu-item-select", (name: string | number) => {
    currentActiveName.value = name;

    nextTick(() => {
      props.collapse && removeAll();
    });
    emit("select", name);
  });

  rootMenuEmitter.on("open-name-change", ({ name, opened }) => {
    if (opened && !openedNames.value.includes(name)) {
      openedNames.value.push(name);
    } else if (!opened) {
      const index = openedNames.value.findIndex((item) => item === name);
      index !== -1 && openedNames.value.splice(index, 1);
    }
  });
});
</script>
<style lang="less">
@import url("./menu.less");
</style>
